package io;

import java.io.* ;
import java.text.* ;
import java.util.* ;

import utilities.StringUtilities;


/*
 * Filter und Index fehlen noch
 */


/**
 * Zugriff auf dBaseIII-Dateien
 */
public class Dbf
{
   private RandomAccessFile fileClDbf ;
   private RandomAccessFile fileClDbt ;

   private String strClDbfName ; // Name DBF-Datei
   private String strClDbtName ; // Name DBT-Datei
   private boolean isClDbt     ; // Memo-Felder vorhanden
   private int iClReccount        = 0 ; // reccount
   private int iClRecno           = 0 ; // RECNO()
   private int iClHeadsize        = 0 ; // Kopfgroesse
   private int iClRecsize         = 0 ; // recsize
   private String[] strArrFld  ; // Array mit den Feldern
   private String strClRecpuff ;

   private boolean bEof           = false ;
   private boolean bBof           = false ;

   private int iClFieldcount      = 0 ; // Anzahl Felder
   private String[] strArrClFeldname   ;
   private char[]   charArrClFeldtyp   ;
   private int[]    intArrClFeldlaenge ;
   private int[]    intArrClDecimals   ;

   private int[]    intArrClFldOffset  ;


   /**
    * Puffer f�r einen Datensatz
    */
   private byte[] bArrClRecordPuffer = null ;

   /**
    * Merker, ob in den Datensatz geschrieben wurde
    */
   private boolean bClRecUpdated = false ;

   /**
    * Eof
    */
   private boolean bClEof = false ;

   /**
    * Bof
    */
   private boolean bClBof = false ;

   /**
    * ob der Datensatz-Zaehler gespeichert werden muss
    */
   private boolean bClRecCountUpdated = false ;

    /**
    * Konstruktor
    */
   public Dbf()
      throws Exception , IOException
   {
      super() ;
   } // end Konstruktor


   /**
    * Konstruktor
    */
   public Dbf( File file )
      throws Exception , IOException
   {
      super() ;
      use( file ) ;
   } // end Konstruktor


   /**
    * Konstruktor
    */
   public Dbf( String strPaFileName )
      throws Exception , IOException
   {
      super() ;
      File file = new File( strPaFileName ) ;

      use( file ) ;
   }


   /**
    * Lesen eines String aus einem RandomAccessFile
    */
   private String readString( RandomAccessFile raFile , int iPaAnzahl )
      throws IOException
   {
      StringBuffer strBuff = new StringBuffer( iPaAnzahl ) ;
      for ( int i = 0 ; i < iPaAnzahl ; i++ )
      {
         strBuff.append( (char) raFile.readUnsignedByte() ) ;
      }
      return strBuff.toString() ;
   } // end method readString


   /**
    * Lesen eines 32-bit-Integer in Intel-Notation von einem RandomAccessFile
    */
   private int readInt32Intel( RandomAccessFile raFile )
      throws IOException
   {
      int i1 = raFile.read() ;
      int i2 = raFile.read() ;
      int i3 = raFile.read() ;
      int i4 = raFile.read() ;
      return i1 + ( i2 << 8 ) + ( i3 << 16 ) + ( i3 << 24 ) ;
   } // end method readInt32Intel

   /**
    * Schreiben eines 32-bit-Integer in Intel-Notation auf ein RandomAccessFile
    */
   private void writeInt32Intel( RandomAccessFile raFile , int iPaValue )
      throws IOException
   {
      raFile.write( (byte) ( iPaValue % 256 ) ) ;
      iPaValue = iPaValue >>> 8 ;
      raFile.write( (byte) ( iPaValue % 256 ) ) ;
      iPaValue = iPaValue >>> 8 ;
      raFile.write( (byte) ( iPaValue % 256 ) ) ;
      iPaValue = iPaValue >>> 8 ;
      raFile.write( (byte) ( iPaValue % 256 ) ) ;
   } // end method writeInt32Intel


   /**
    * Lesen eines 16-bit-Integer in Intel-Notation von einem RandomAccessFile
    */
   private int readInt16( RandomAccessFile raFile )
      throws IOException
   {
      int i1 = raFile.read() ;
      int i2 = raFile.read() ;
      return i1 + ( i2 << 8 ) ;
   } // end method readString





   public void use ( String strPaFileName )
      throws Exception , IOException
   {
      File file = new File( strPaFileName ) ;

      use( file ) ;
   }



   /**
    * Dbf-Datei oeffnen
    */
   @SuppressWarnings("deprecation")
public void use( File filePa )
      throws Exception , IOException
   {
	   //System.out.println( "use " + filePa ) ;

      System.runFinalizersOnExit( true ) ; // f�r schliessen der DBF-Datei sorgen

//      if ( filePa == null ) System.out.println( "filePa is null" ) ;

      fileClDbf = new RandomAccessFile( filePa , "rw" ) ;
//      if ( fileClDbf == null ) System.out.println( "fileClDbf is null" ) ;

      strClDbfName = filePa.getName() ;

      // Pr�fen auf Memo-Felder
      int iTmp = fileClDbf.read() ;
      if     ( iTmp == 3 )
      {
         isClDbt = false ;
      }
      else if ( iTmp == 131 )
      {
         isClDbt = true ;
      }
      /*
      else if ( iTmp == 245 )
      {
         Fox-Pro-Format
      }
      */
      else
      {
         throw new Exception( "Formatfehler " + strClDbfName ) ;
      }

      // Datum �berspringen
      fileClDbf.skipBytes( 3 ) ;

      // Anzahl Datens�tze 4 Byte
      iClReccount = readInt32Intel( fileClDbf ) ;

      // Kopfl�nge HEADER 2 Byte
      iClHeadsize = readInt16( fileClDbf ) ;

      // Satzl�nge RECSIZE 2 Byte
      iClRecsize = readInt16( fileClDbf ) ;

      // Anzahl Felder
      // falsch iClFieldcount = ( ( iClHeadsize - 2 ) / 32 ) - 1 ;
      iClFieldcount = ( iClHeadsize / 32 ) - 1 ;

      // Feldnamen
      strArrClFeldname   = new String[ iClFieldcount ] ;
      charArrClFeldtyp   = new char  [ iClFieldcount ] ;
      intArrClFeldlaenge = new int   [ iClFieldcount ] ;
      intArrClDecimals   = new int   [ iClFieldcount ] ;
      intArrClFldOffset  = new int   [ iClFieldcount ] ;

      // Felder Einlesen
      for ( int i = 0 ; i < iClFieldcount ; i++ )
      {
         // Feldname
         strArrClFeldname[i] = "" ;

         fileClDbf.seek( ( i + 1 ) * 32 ) ;

         for ( int j = 1 ; j <= 11 ; j++ )
         {
            iTmp = fileClDbf.read() ;

            if ( iTmp == 0 ) break ; // Abbrechen bei (char)0

            strArrClFeldname[i] += ( "" + ( ( char ) iTmp ) ) ;
         }

         // Feldtyp
         fileClDbf.seek( ( ( i + 1 ) * 32 ) + 11 ) ;

         charArrClFeldtyp[i] += ( char ) fileClDbf.read() ; // noch Pr�fung auf N, C, D, L, M einbauen

         // Feldl�nge
         fileClDbf.seek( ( ( i + 1 ) * 32 ) + 16 ) ;

         intArrClFeldlaenge[i] += fileClDbf.read() ; // noch Pr�fung auf zul�ssige L�nge einbauen

         // Feld-Offset im RecordPuffer
         if ( i == 0 )
         {
            intArrClFldOffset[ 0 ] = 1 ; // erstes Zeichen ist L�schkennzeichen
         }
         else
         {
            intArrClFldOffset[ i ] = intArrClFldOffset[ i - 1 ] + intArrClFeldlaenge[ i - 1 ] ;
            // System.out.println( "intArrClFldOffset " + i + ": " + intArrClFldOffset[ i ] ) ;
         }

         // Dezimalstellen
         intArrClDecimals[i] += fileClDbf.read() ; // noch Pr�fung auf zul�ssige L�nge einbauen , noch spezielle lange Clipper-Chars beachten

      }

      bArrClRecordPuffer = null ;

// String strLineNumber = LineNumber.getLineNumber() ;
// System.out.println( strLineNumber ) ;

      if ( isClDbt ) // Memofelder vorhanden
      {
         // Standardextension DBT anh�ngen
         strClDbtName = StringUtilities.stdTyp( strClDbfName , "DBT" ) ;

         fileClDbt = new RandomAccessFile( strClDbtName , "rw" ) ;
      }

      bClRecCountUpdated = false ;

      goTop() ;

   } // end method use


   /**
    * zum obersten Datensatz springen
    * ein eventuell vorhandener alter Recordpuffer wird zurueckgeschrieben
    * der neue Recordpuffer wird geholt
    */
   public void goTop()
      throws IOException
   {
      goTo( 1 ) ;
   } // end method goTop

   /**
    * zum letzten Datensatz springen
    * ein eventuell vorhandener alter Recordpuffer wird zurueckgeschrieben
    * der neue Recordpuffer wird geholt
    */
   public void goBott()
      throws IOException
   {
      goTo( getRecCount() ) ;
   } // end method goTop

   /**
    * 1 Datensatz weiterschalten
    * ein eventuell vorhandener alter Recordpuffer wird zurueckgeschrieben
    * der neue Recordpuffer wird geholt
    */
   public void skip()
      throws IOException
   {
      skip( 1 ) ;
   } // end method goTop


   /**
    * xx Datens�tze weiterschalten
    * ein eventuell vorhandener alter Recordpuffer wird zurueckgeschrieben
    * der neue Recordpuffer wird geholt
    */
   public void skip( int iPaValue )
      throws IOException
   {
      bClEof = false ;
      bClBof = false ;

      // �ber das Ende
      if     ( getRecNo() + iPaValue > getRecCount() )
      {
         goBott() ;
         bClEof = true ;
      }
      // �ber den Anfang
      else if( getRecNo() + iPaValue < 1 )
      {
         goTop() ;
         bClBof = true ;
      }
      // ok
      else
      {
         goTo( getRecNo() + iPaValue ) ;
      }
   } // end method goTop


   /**
    * zu bestimmten Datensatz springen
    * ein eventuell vorhandener alter Recordpuffer wird zurueckgeschrieben
    * der neue Recordpuffer wird geholt
    * @param iPaRecno Datensatznummer 1-basiert
    */
   public void goTo( int iPaRecno )
      throws IOException
   {
      if ( iPaRecno > 0 && iPaRecno <= iClReccount )
      {
         // Rueckspeichern
// wenn angesteuerter Datensatz gleich aktueller Datensatz -> speichern nicht n�tig ( eventuell als flush-Methode )
         flush() ;

         iClRecno = 0 ; // R�cksetzen f�r eventuellen Fehlerfall

         // Holen Recordpuffer
         bArrClRecordPuffer = new byte[ iClRecsize ] ;
         fileClDbf.seek( iClHeadsize + ( ( iPaRecno - 1 ) * iClRecsize ) ) ;
         fileClDbf.readFully( bArrClRecordPuffer ) ;
         iClRecno = iPaRecno ;

         bClEof = false ;
         bClBof = false ;
      }
   } // end method goTop


   /**
    * eof - End of File
    */
   public boolean eof()
   {
      if ( getRecCount() > 0 )
      {
         return bClEof ;
      }
      return true ;
   } // end method eof

   /**
    * bof - Begin of File
    */
   public boolean bof()
   {
      if ( getRecCount() > 0 )
      {
         return bClBof ;
      }
      return true ;
   } // end method eof


   /**
    * Rueckspeichern Record-Puffer
    */
   public void flush()
      throws IOException
   {
      if ( bClRecUpdated && iClRecno > 0 && iClRecno <= iClReccount && bArrClRecordPuffer != null )
      {
         fileClDbf.seek( iClHeadsize + ( ( iClRecno - 1 ) * iClRecsize ) ) ;
         fileClDbf.write( bArrClRecordPuffer ) ;
      }
      bClRecUpdated = false ;
   } // end method flush


   /**
    * Feld als String holen ( Index null-basiert )
    */
   public String getString( int iPaFldNo )
      throws IOException
   {
      if ( iPaFldNo >= 0 && iPaFldNo < iClFieldcount )
      {
         StringBuffer strBuff = new StringBuffer( intArrClFeldlaenge[ iPaFldNo ] ) ;
         for ( int i = intArrClFldOffset[ iPaFldNo ] ; i < intArrClFldOffset[ iPaFldNo ] + intArrClFeldlaenge[ iPaFldNo ] ; i++ )
         {
            strBuff.append( StringUtilities.byte2char( bArrClRecordPuffer[ i ] ) ) ;
         }
         if ( charArrClFeldtyp[ iPaFldNo ] == (char) 77 ) // Vergleich auf "M"
         {
            return getMemo( strBuff.toString() ) ;
         }
         else if ( charArrClFeldtyp[ iPaFldNo ] == (char) 68 ) // Vergleich auf "D"
         {
            return datumString( strBuff.toString() ) ;
         }
         else if ( charArrClFeldtyp[ iPaFldNo ] == (char) 76 ) // Vergleich auf "L"
         {
            return boolString( strBuff.toString() ) ;
         }
         else
         {
            return strBuff.toString() ;
         }
      }
      else
      {
         return "" ;
      }
   } // end method getFeldString


   /**
    * Feld als double holen ( Index null-basiert )
    */
   public double getDouble( int iPaFldNo )
      throws IOException
   {
      if ( iPaFldNo >= 0 && iPaFldNo < iClFieldcount )
      {
         if ( charArrClFeldtyp[ iPaFldNo ] == (char) 78 ) // Vergleich auf "N"
         {
            StringBuffer strBuff = new StringBuffer( intArrClFeldlaenge[ iPaFldNo ] ) ;
            for ( int i = intArrClFldOffset[ iPaFldNo ] ; i < intArrClFldOffset[ iPaFldNo ] + intArrClFeldlaenge[ iPaFldNo ] ; i++ )
            {
               strBuff.append( (char) bArrClRecordPuffer[ i ] ) ;
            }
            try
            {
               return Double.parseDouble( strBuff.toString().trim() ) ;
            }
            catch ( Exception e) {}
         }
      }
      return (double) 0 ;
   } // end method getFeldString



   /**
    * Feld als int holen ( Index null-basiert )
    */
   public int getInt( int iPaFldNo )
      throws IOException
   {
      if ( iPaFldNo >= 0 && iPaFldNo < iClFieldcount )
      {
         if ( charArrClFeldtyp[ iPaFldNo ] == 'N' ) ; // (char) 78 ) // Vergleich auf "N"
         {
            StringBuffer strBuff = new StringBuffer( intArrClFeldlaenge[ iPaFldNo ] ) ;
            for ( int i = intArrClFldOffset[ iPaFldNo ] ; i < intArrClFldOffset[ iPaFldNo ] + intArrClFeldlaenge[ iPaFldNo ] ; i++ )
            {
               strBuff.append( (char) bArrClRecordPuffer[ i ] ) ;
            }
            try
            {
               //System.out.print( "->" + strBuff.toString() ) ;
               return Integer.parseInt( strBuff.toString().trim() ) ;
            }
            catch ( Exception e) {} ;
         }
      }
      return 0 ;
   } // end method getFeldString



   private String getMemo( String strPaPos )
      throws IOException
   {
// System.out.println( strPaPos ) ;

      String strTmp = strPaPos.trim() ;

      if ( strTmp.equals( "" ) ) return "" ;

      int iMemAdress = ( new Integer( strTmp ) ).intValue() * 512 ;

      byte[] bArrPuffer = new byte[ 65536 ] ;

      fileClDbt.seek( iMemAdress ) ;

      try
      {
         fileClDbt.readFully( bArrPuffer ) ;
      }
      catch ( EOFException e ) {}

      int i = 0 ;
      while ( ( i < bArrPuffer.length ) && ( bArrPuffer[ i ] != (byte)26 ) )
      {
         i++ ;
      }

//      return new String( bArrPuffer , 0 , i - 1 ) ;

      StringBuffer strBuff = new StringBuffer( i ) ;
      for ( int j = 0 ; j < i ; j++ )
      {
         strBuff.append( StringUtilities.byte2char( bArrPuffer[ j ] ) ) ;
      }
      return strBuff.toString() ;

   } // end method getMemo




   public String getFeldName( int iPaFldNo )
   {
      if ( iPaFldNo >= 0 && iPaFldNo < iClFieldcount )
      {
         return strArrClFeldname[ iPaFldNo ] ;
      }
      else
      {
         return "" ;
      }
   } // end method getFeldName

   public void listStru()
   {
      System.out.println( strClDbfName );
      System.out.println( "Reccount    " + iClReccount ) ;
      System.out.println( "Recno       " + iClRecno ) ;
      System.out.println( "Headsize    " + iClHeadsize ) ;
      System.out.println( "Recsize     " + iClRecsize ) ;
      System.out.println( "Fieldcount  " + iClFieldcount ) ;
      System.out.println() ;
      for ( int i = 0 ; i < iClFieldcount ; i++ )
      {
         System.out.println( StringUtilities.pad( strArrClFeldname[i] , 11 ) + " " + charArrClFeldtyp[i] + " " + intArrClFeldlaenge[i] + " " + intArrClDecimals[i] ) ;
      }
   } // end method listStru

   public void list()
      throws IOException
   {
      for ( int i = 1 ; i <= iClReccount ; i++ )
      {
         goTo( i ) ;
         for ( int j = 0 ; j < iClFieldcount ; j++ )
         {
            System.out.print( getInt( j ) + "|" ) ;
         }
         System.out.println( "" ) ;
      }
   } // end method list

   /**
    * liefert aus einem intern in dBase dargestellten String die Datumsdarstellung tt.mm.jjjj
    */
   private String datumString( String strPaIntern )
   {
      // String strTag      = strPaIntern.substring( 6 ) ;
      // String strMonat    = strPaIntern.substring( 4 , 6 ) ;
      // String strJahr     = strPaIntern.substring( 0 , 4 ) ;
      // return strTag + "." + strMonat + "." + strJahr ;
      return strPaIntern.substring( 6 ) + "." + strPaIntern.substring( 4 , 6 ) + "." + strPaIntern.substring( 0 , 4 ) ;
   } // end method datumString


   /**
    * liefert aus einem intern in dBase dargestellten String einen String "J" oder "N"
    */
   private static String boolString( String strPaIntern )
   {
      return "T".equals( strPaIntern ) ? "J" : "N" ;
   } // end method datumString


   /*
    * speichern String in Datenbankfeld
    */
   public boolean setFeld( int iPaFldNo , String strPaValue )
      throws IOException
   {
      if ( iPaFldNo < 0 && iPaFldNo >= iClFieldcount ) return false ;
      if ( iClRecno < 1 || iClRecno > iClReccount )    return false ;

      if ( charArrClFeldtyp[ iPaFldNo ] == 'M' )       return setMemo( iPaFldNo , strPaValue ) ;

      if ( charArrClFeldtyp[ iPaFldNo ] != 'C' )       return false ;

      if ( strPaValue == null )
      {
         for ( int i = intArrClFldOffset[ iPaFldNo ] ; i < intArrClFldOffset[ iPaFldNo ] + intArrClFeldlaenge[ iPaFldNo ] ; i++ )
         {
            bArrClRecordPuffer[ i ] = (byte) 32 ; // Leerzeichen
         }
      }
      else
      {
         int j = 0 ;
         for ( int i = intArrClFldOffset[ iPaFldNo ] ; i < intArrClFldOffset[ iPaFldNo ] + intArrClFeldlaenge[ iPaFldNo ] ; i++ )
         {
            bArrClRecordPuffer[ i ] = ( j < strPaValue.length() ) ? StringUtilities.char2byte( strPaValue.charAt( j ) ) : (byte) 32 ;
            j++ ;
         }
      }
      bClRecUpdated = true ;
      return true ;
   } // end method setFeld


   /*
    * speichern double in Datenbankfeld
    */
   public boolean setFeld( int iPaFldNo , double dPaValue )
      throws IOException
   {
      if ( iPaFldNo < 0 && iPaFldNo >= iClFieldcount ) return false ;
      if ( iClRecno < 1 || iClRecno > iClReccount )    return false ;

      if ( charArrClFeldtyp[ iPaFldNo ] != 'N' )       return false ;

      String strTmp = StringUtilities.lpad( ( getDecimalFormat( intArrClFeldlaenge[ iPaFldNo ] , intArrClDecimals[ iPaFldNo ] ).format( dPaValue ) ) , intArrClFeldlaenge[ iPaFldNo ] ) ;

//System.out.println( strTmp ) ;
      // String in Feld das kein C-feld ist schreiben noch als Methode auslagern
      int j = 0 ;
      for ( int i = intArrClFldOffset[ iPaFldNo ] ; i < intArrClFldOffset[ iPaFldNo ] + intArrClFeldlaenge[ iPaFldNo ] ; i++ )
      {
         bArrClRecordPuffer[ i ] = (byte) strTmp.charAt( j ) ;
         j++ ;
      }

      bClRecUpdated = true ;
      return true ;
   } // end method setFeld


   /*
    * speichern boolean in Datenbankfeld
    */
   public boolean setFeld( int iPaFldNo , boolean bPaValue )
      throws IOException
   {
      if ( iPaFldNo < 0 && iPaFldNo >= iClFieldcount ) return false ;
      if ( iClRecno < 1 || iClRecno > iClReccount )    return false ;

      if ( charArrClFeldtyp[ iPaFldNo ] != 'L' )       return false ;

      if ( bPaValue )
      {
         bArrClRecordPuffer[ intArrClFldOffset[ iPaFldNo ] ] = (byte) 'T' ;
      }
      else
      {
         bArrClRecordPuffer[ intArrClFldOffset[ iPaFldNo ] ] = (byte) 'F' ;
      }

      bClRecUpdated = true ;
      return true ;
   } // end method setFeld


   /**
    * Erzeugen DecimalFormat entsprechend �bergebener Parameter
    */
   private static DecimalFormat getDecimalFormat( int iPaFldLen , int iPaFldDec )
   {
      String strTmp ;
      if ( iPaFldDec > 0 )
      {
         strTmp = StringUtilities.repl( "0" , iPaFldDec ) ;
         if ( iPaFldLen > iPaFldDec )
         {
            strTmp = StringUtilities.repl( "#" , iPaFldLen - iPaFldDec - 1 ) + ( iPaFldLen > iPaFldDec ? "." : "" ) + strTmp ;
         }
      }
      else
      {
         strTmp = StringUtilities.repl( "#" , iPaFldLen ) ;
      }
//System.out.println( strTmp ) ;
      return new DecimalFormat( strTmp ) ;
   } // end method getDecimalFormat


   /**
    * speichern String in Memo-Feld
    * wird nur intern aufgerufen von setFeld( int iPaFldNo , String strPaValue )
    * worin die Pr�fung Feldpos und Recno schon erfolgte
    */
   private boolean setMemo( int iPaFldNo , String strPaValue )
      throws IOException
   {
// wenn Block < 512 Byte immer den alten Block verwenden ( 1. speicherspartechnik)
// nicht benutzten Block mit 1AH am Anfang kennzeichnen
// eventuell auch L�nge des unbenutzten Blockes speichern



// System.out.println( "setMemo" ) ;
      if ( charArrClFeldtyp[ iPaFldNo ] != 'M' ) return false ;

      if ( strPaValue == null || strPaValue.length() < 1 )
      {
         // L�schen Zeiger
         for ( int i = intArrClFldOffset[ iPaFldNo ] ; i < intArrClFldOffset[ iPaFldNo ] + intArrClFeldlaenge[ iPaFldNo ] ; i++ )
         {
            bArrClRecordPuffer[ i ] = (byte) 32 ; // Leerzeichen
         }
      }
      else
      {
         // speicherverschwendende Methode immer neuen Block anfordern

         // auff�llen bis auf volle 512 Byte
         long lTmp = fileClDbt.length() ;
/* Methode mit definiertem Inhalt der L�cke
         // nicht n�tig
         fileClDbt.seek( lTmp ) ;
         byte[] bArrTmp = new byte[ 512 - ( lTmp % 512 ) ] ;
         Arrays.fill( bArrTmp , (byte) 0 ) ;
         fileClDbt.write( bArrTmp ) ;
*/
// Methode mit undefiniertem Inhalt der L�cke
         lTmp = ( ( lTmp / 512 ) + 1 ) * 512 ;
         fileClDbt.setLength( lTmp ) ;
         fileClDbt.seek( lTmp ) ;
         fileClDbt.write( strPaValue.getBytes() ) ;
         fileClDbt.write( (byte) 26 ) ; // Endekennzeichen

         String strTmp = ( ( new DecimalFormat( "0000000000" ) ).format( lTmp / 512 ) ).toString() ;
         int j = 0 ;
         for ( int i = intArrClFldOffset[ iPaFldNo ] ; i < intArrClFldOffset[ iPaFldNo ] + intArrClFeldlaenge[ iPaFldNo ] ; i++ )
         {
            bArrClRecordPuffer[ i ] = StringUtilities.char2byte( strTmp.charAt( j ) ) ;
            j++ ;
         }
      }
      bClRecUpdated = true ;
      return true ;
   } // end method setMemo




   /**
    * Abfragen Feld-Position ( numerisch Basis 0 ) anhand Feldname ( String )<BR>
    * wird Feldname nicht gefunden wird -1 zur�ckgegeben
    */
   public int getFeldPos( String strPaFeldName )
   {
// noch trim und uppercase

      if ( strPaFeldName == null ) return -1 ;

      for ( int i = 0 ; i < strArrClFeldname.length ; i++ )
      {
         if ( strPaFeldName.equals( strArrClFeldname[ i ] ) )
         {
            return i ;
         }
      }
      return -1 ;
   } // end method getFeldPos


   public int getFieldCount()
   {
      return iClFieldcount ;
   }


   /**
    * Abfragen Anzahl Datens�tze
    */
   public int getRecCount()
   {
      return iClReccount ;
   } // end method getRecCount


   public boolean hasMemo()
   {
      return isClDbt ;
   }

   /**
    * Abfragen aktuelle Datensatz-Nummer
    */
   public int getRecNo()
   {
      return iClRecno ;
   }


   /**
    * ob das Feld ( Feldnummer null-basiert ) ein Memo-Feld ist
    */
   public boolean isMemoFeld( int iPaFldNo )
   {
      if ( iPaFldNo >= 0 && iPaFldNo < iClFieldcount )
      {
         return ( charArrClFeldtyp[ iPaFldNo ] == (char) 77 ) ; // Vergleich auf "M"
      }
      return false ;
   } // end method isMemoFeld


   /**
    * Datenbankdatei schliessen
    */
   public void close()
      throws IOException
   {
      flush() ;

      if ( bClRecCountUpdated )
      {
         fileClDbf.seek( 4 ) ;
         writeInt32Intel( fileClDbf , iClReccount ) ;
      }

      fileClDbf.close() ;
      fileClDbt.close() ;
   } // end method close

   protected void finalize()
      throws Throwable
   {
	   try {
           ObjectOutputStream os = new ObjectOutputStream(
                   new FileOutputStream("C:\\Enkhee\\zugang.csv"));
          // os.writeObject(this); 
//Achtung: Objekte k�nnen nur dann serialisiert werden wenn sie das Interface "Serializable" implementieren
           os.close();
       }
       catch (FileNotFoundException e1) {
           e1.printStackTrace();
       }
       catch (IOException e1) {
           e1.printStackTrace();
       }

      //System.out.println( "Dbf-Datei " + strClDbfName + " wird geschlossen" ) ;
      close() ;
   }

   /**
    * Datensatz anh�ngen
    */
   public void append()
      throws IOException
   {
// eventuell so �ndern dass erst beim schliessen physisch geschrieben wird

      flush() ;
      iClReccount++ ;
      iClRecno = iClReccount ;
      bArrClRecordPuffer = new byte[ iClRecsize ] ;
      Arrays.fill( bArrClRecordPuffer , (byte) 32 ) ;
      fileClDbf.seek( iClHeadsize + ( ( iClReccount - 1 ) * iClRecsize ) ) ;
      fileClDbf.write( bArrClRecordPuffer ) ;
      fileClDbf.write( (char) 26 ) ; // Dateiendenkennung nach dBase-Standard

      bClRecCountUpdated = true ;

      bClEof = false ;
      bClBof = false ;
   }

   /**
    * Abfragen L�schkennzeichnung
    */
   public boolean isDele()
   {
      if ( bArrClRecordPuffer != null && bArrClRecordPuffer.length > 0 )
      {
         return ( bArrClRecordPuffer[ 0 ] == (byte) 42 ) ; // chr(42) *
      }
      return false ;
   } // end method isDele

   /**
    * Setzen L�schkennzeichnung
    */
   public void dele()
   {
      if ( bArrClRecordPuffer != null && bArrClRecordPuffer.length > 0 )
      {
         bArrClRecordPuffer[ 0 ] = (byte) 42 ; // chr(42) *
         bClRecUpdated = true ;
      }
   } // end method dele

   /**
    * R�ck-Setzen L�schkennzeichnung
    */
   public void reca()
   {
      if ( bArrClRecordPuffer != null && bArrClRecordPuffer.length > 0 )
      {
         bArrClRecordPuffer[ 0 ] = (byte) 32 ; // Leerzeichen
         bClRecUpdated = true ;
      }
   } // end method reca

   /**
    * Setzen L�schkennzeichnung
    */
   public void dele( boolean bPaValue )
   {
      if ( bPaValue )
      {
         dele() ;
      }
      else
      {
         reca() ;
      }
   } // end method dele


   /**
    * Lesen ganzen Datensatz als Byte-Array
    */
   public byte[] getRec()
   {
      // Datensatz muss positioniert und eingelesen sein
      return bArrClRecordPuffer ;
   } // end method getRec


   /**
    * Schreiben ganzen Datensatz als Byte-Array
    */
   public void setRec( byte[] bArrPaValue )
   {
      if ( bArrPaValue != null && bArrPaValue.length == iClRecsize )
      {
         bArrClRecordPuffer = bArrPaValue ;
      }
      bClRecUpdated = true ;
   } // end method setRec


   /**
    * physisches L�schen der l�schgekennzeichneten Datens�tze
    */
   public void pack()
      throws IOException
   {
// Achtung, bei Filter nur die echten Methoden von Dbf aufrufen
      int iRecno , iDelAnz ;
      goTop() ;
      while ( ! isDele() && ! eof() )
      {
         skip() ;
      }

      // System.out.println( "rangerollt " + getRecNo() ) ;

      if ( eof() )
      {
         goTop() ;
         return ;
      }

      iRecno = getRecNo() ;
      iDelAnz = 1 ;

      while ( iRecno + iDelAnz <= iClReccount )
      {
         goTo( iRecno + iDelAnz ) ;
         if ( isDele() )
         {
            iDelAnz++ ;
         }
         else
         {
            if ( iRecno + iDelAnz <= iClReccount )
            {
               // System.out.println( "Transport " + iRecno + " " + iDelAnz ) ;
               goTo( iRecno + iDelAnz ) ;
               byte[] bArrPuffer = getRec() ;
               goTo( iRecno ) ;
               setRec( bArrPuffer ) ;
            }
            iRecno++ ;
         }
      }
      setRecCount( iClReccount - iDelAnz ) ;

      return ;
   } // end method pack


   /**
    * Einf�gen eines Datensatzes vor dem aktuellen Datensatz
    */
   public void inseReco()
      throws IOException
   {
      if ( iClRecno < 1 ) return ;
      int iRecno = iClRecno ;

      append() ;

      for ( int i = iClReccount ; i > iRecno ; i-- )
      {
         goTo( i - 1 ) ;
         byte[] bArrPuffer = getRec() ;
         goTo( i ) ;
         setRec( bArrPuffer ) ;
      }

      goTo( iRecno ) ;
      setBlank() ; // L�schen Datensatz
      goTo( iRecno + 1 ) ;
   } // end method inseReco


   /**
    * Leeren des aktuellen Datensatzes
    */
   public void setBlank()
   {
      if ( iClRecno > 0 )
      {
         bArrClRecordPuffer = new byte[ iClRecsize ] ;
         Arrays.fill( bArrClRecordPuffer , (byte) 32 ) ;

         bClRecUpdated = true ;
      }
   } // end method setBlank


   /**
    * L�schen des aktuellen Datensatzes
    */
   public void eraReco()
      throws IOException
   {
      if ( iClRecno < 1 ) return ;
      int iRecno = iClRecno ;

      for ( int i = iRecno ; i < iClReccount  ; i++ )
      {
         goTo( i + 1 ) ;
         byte[] bArrPuffer = getRec() ;
         goTo( i ) ;
         setRec( bArrPuffer ) ;
      }

      setRecCount( iClReccount - 1 ) ;

      goTo( iRecno ) ;
   } // end method inseReco


   /**
    * Setzen Datensatzanzahl mit vergroessern/-kleinern der Datenbank
    */
   public void setRecCount( int iPaReccount )
      throws IOException
   {
      if ( iPaReccount < 0 ) return ;
      if ( iPaReccount > iClReccount )
      {
         for ( int i = ( iPaReccount - iClReccount ) ; i > 0 ; i-- )
         {
            append() ; // eventuell auch schnellere Methode denkbar
         }
      }
      else if ( iPaReccount < iClReccount )
      {
         flush() ;
         iClReccount = iPaReccount ;
         // ans Dateiende positionieren
         fileClDbf.setLength( iClHeadsize + ( ( iClReccount ) * iClRecsize ) ) ;
         fileClDbf.seek( iClHeadsize + ( ( iClReccount ) * iClRecsize ) ) ;
         fileClDbf.write( (char) 26 ) ; // Dateiendenkennung nach dBase-Standard

         bClRecCountUpdated = true ;
         goTop() ;
      }
   } // end method setRecCount






/*

**************
func hflen(fld)    && (num or name)

func hfdec
func hfget
func hfld
func hftyp
******
func hreccount && RECCOUNT()
retu(recc)
******
func hheader && HEADER()
func hrsize && RECSIZE()
func hmlen && Memo-L�nge


append
dele_record
index
sort
set filter
dbfcreate






*/

}

